#!/usr/bin/env python
# -*- coding:utf-8 -*-


from tkinter import Tk, messagebox, Toplevel, ttk, TOP
from tkinter import *
import tkinter as tk

from cryptography.fernet import Fernet


#可用于登陆的账户：账户名：2，用户名：2，密码：2

# 加密和解密需要的key
key = b'fYUE4FR4Hp8BpmOQL6BCkXFHIrzbhO-h6FrcR-0I_Ko='
fernet = Fernet(key)  # 实例化一个Fernet对象

import sqlite3

data = "data.db"

def Dao(query, params=()):
    #  建立连接
    with sqlite3.connect(data) as conn:
        crs = conn.cursor()  # 导入游标
        res = conn.execute(query, params)  # 执行sql语句
        conn.commit()  # 提交事务a
        return res  # 返回结果


def login(params=()):
    # 查询数据库
    sql = "select password,source from data WHERE account=? and username=?  "   
    #查询所有用户名和账户筛选出密码和来源
    try:
        # 当查询数据存在时返回用户信息
        Dao(sql, params).__next__()
        return Dao(sql, params)
    except:
        #  不存在时返回None
        return None


def finallbyac():
    #  查询所有账户
    sql = "select  account,count(*) from data group by account "
    #从data数据库里根据用户各自的分组查询用户名并统计每个用户所包含的条数
    return Dao(sql)


def findbyac(ac):
    sql = "select  username,password ,source from data where account = ? "  
    #输入账户名从data库中根据账户名查询用户名，密码和来源
    try:
        # 当查询数据存在时返回用户信息
        Dao(sql, (ac,)).__next__()
        return Dao(sql, (ac,))
    except:
        # 不存在时返回None
        return None


def insertdata(pa=()):#把各个值插入data中
    sql = "insert into  data (id,account, username, password,source) values ((SELECT max(Id) FROM data)+1 ,?,?,?,?)"
    Dao(sql, pa)


def encipherment(pssword):
    '''加密'''
    pa = bytes(pssword, encoding='utf-8')  # 转换为byte类型
    token = fernet.encrypt(pa)  # 加密
    return str(token, 'utf-8')


def decryption(token):
    '''解密'''
    token_byte = bytes(token, encoding='utf-8')  # 转换为byte类型
    deco = fernet.decrypt(token_byte)  # 解密
    return str(deco, 'utf-8')

def msgshowinfo(tiltle, message):
    #  弹出窗体
    messagebox.showinfo(title=tiltle, message=message)


def verblen(pas):
    #  验证传入参数是否含有空参数
    if pas:
        for i in pas:
            if len(str(i)) <= 0:
                return 0
        return 1
    return 0


def topdlog():
    top = Toplevel()
    top.title('查询')
    width, height = top.maxsize()
    top.geometry('%dx%d+%d+%d' % (600, 400, ((width - 500) / 2), ((height - 400) / 2)))
    tk.Label(top, text="账户", width=10, font=('fangsong', 14)).place(x=15, y=20)
    content = tk.StringVar()
    tk.Entry(top, textvariable=content, width=10, font=('fangsong', 14)).place(x=100, y=20)

    lable1 = Label(top, text="")
    lable1.place(x=20, y=60)

    cvar1 = IntVar()
    Checkbutton(top, text='密码明文', variable=cvar1, onvalue=1, offvalue=0, font=('fangsong', 14)).place(x=200, y=20)

    def showdata(top):
        ''' 展示数据 '''
        tree = ttk.Treeview(top)
        tree.place(x=0, y=100)
        tree.Scrollable = 'true'
        tree["columns"] = ("#0", "#1", "#2")
        tree.column("#0", anchor="center")
        tree.column("#1", anchor="center")
        tree.column("#2", anchor="center")

        # 设置表头
        tree.heading("#0", text="用户名", anchor="center")
        tree.heading("#1", text="密 码", anchor="center")
        tree.heading("#2", text="来 源", anchor="center")
        return tree

    def btn():
        accountname = content.get()  # 获取输入的账户
        treetmp = showdata(top)  # 获取展示窗体
        # 清空元素
        emu = treetmp.get_children()
        for i in emu:
            treetmp.delete(i)
        if verblen([accountname, ]):  # 判断账户是否为空
            data = findbyac(accountname)  # 根据账户查询相关信息
            if data:  # 判断是否存在（有数据就存在，反之不存在）
                lable1["text"] = ""  # 清空提示表单

                flag = cvar1.get()  # 获取是否明文显示


                # 循环输出页面
                for e in data:
                    if flag:  # 判断是否选择明文展示
                        treetmp.insert("", 0, text=e[0], values=(decryption(e[1]), e[2]))
                    else:
                        treetmp.insert("", 0, text=e[0], values=(e[1], e[2]))
            else:
                lable1["text"] = "账户不存在"
        else:
            lable1["text"] = "账户名不能为空"

    findbtn = Button(top, text="查询", command=btn)
    findbtn.place(x=320, y=20, height=25)


def adddata():
    top = Toplevel()
    top.title('添加')
    cre_width, cre_height = top.maxsize()
    top.geometry('%dx%d+%d+%d' % (434, 375, ((cre_width - 300) / 2), ((cre_height - 400) / 2)))

    tk.Label(top, text="添加数据", width=20, font=('songti', 20)).place(x=100, y=20)
    tk.Label(top, text="账  户", width=10, font=('fangsong', 14)).place(x=60, y=70)
    account_text = tk.StringVar()
    account = Entry(top, textvariable=account_text, font=('fangsong', 14), width=15)
    account.place(x=160, y=70)
    tk.Label(top, text="用户名", width=10, font=('fangsong', 14)).place(x=60, y=120)
    username_text = tk.StringVar()
    username = Entry(top, textvariable=username_text, font=('fangsong', 14), width=15)
    username.place(x=160, y=120)
    tk.Label(top, text="密  码", width=10, font=('fangsong', 14)).place(x=60, y=170)
    password_text = tk.StringVar()
    password = Entry(top, textvariable=password_text, font=('fangsong', 14), width=15, show='*')
    password.place(x=160, y=170)
    tk.Label(top, text="来  源", width=10, font=('fangsong', 14)).place(x=60, y=220)
    res_text = tk.StringVar()
    res = Entry(top, textvariable=res_text, font=('fangsong', 14), width=15)
    res.place(x=160, y=220)





    announce = tk.Label(top, text="")
    announce.place(x=50, y=250)

    def getpar():
        account1 = account_text.get()
        username1 = username_text.get()
        password1 = password_text.get()
        res1 = res_text.get()
        if verblen([account1, password1, username1, res1]):
            password2 = encipherment(password1)
            insertdata((account1, username1, password2, res1))
            print(account1, username1, password2, res1)
            str = "账户：{} 用户名：{} 添加成功".format(account1, username1)
            announce["text"] = str
        else:
            announce["text"] = "输入有误，请检查输入数据！"

    submit_btn = Button(top, text="添加", command=getpar, width=10)
    submit_btn.place(x=160, y=300)


def maingui(window: tk):
    window.title('主页')
    # 获取屏幕尺寸
    s_width, s_height = window.maxsize()
    # 设置窗体大小
    w_width, w_height = 600, 400

    window.geometry("%dx%d+%d+%d" % (w_width, w_height, (s_width - w_width) / 2, (s_height - w_height) / 2))

    def destry():
        window.destroy()


    tk.Label(text="保存的账户", font=("", 14)).pack(ipady=35, side=TOP, expand=FALSE, fill=X)
    frame = tk.Frame(window)
    frame.pack(anchor=W, side=LEFT, expand=True, fill=BOTH)
    # fill 以对齐->加滚轮
    content = tk.Text(frame, width=0, height=18)
    content.pack(anchor=W, side=LEFT, expand=False)
    content.configure(background=window.cget('background'), highlightbackground=window.cget('background'))
    # 列,表中每一列的题头
    column = ['账户', '记录/条']
    treeview = ttk.Treeview(frame, height=5, columns=column, show='headings')# show 用‘headings’，不显示表的第一列（因为太宽）
    # ipad为组件之间的距离，expand为扩展参数bool，fill为填充字段X，Y，BOTH
    treeview.pack(anchor=W, ipadx=100, side=LEFT, expand=True, fill=BOTH)
    # 每一列的具体设置用column函数，heading为显示指定列名
    treeview.column(column[0], width=80, anchor='center')
    treeview.heading(column[0], text=column[0])
    treeview.column(column[1], width=80, anchor='center')
    treeview.heading(column[1], text=column[1])
    # 加滚轮
    vbar1 = ttk.Scrollbar(treeview, orient=VERTICAL, command=treeview.yview)
    treeview.configure(yscrollcommand=vbar1.set)
    vbar1.pack(side=RIGHT, fill=Y)

    # 刷新
    def flush():
        child = treeview.get_children()
        for i in child:
            treeview.delete(i)
        all_account = finallbyac()  # 查询所有
        for emnu in all_account:
            treeview.insert('', 0, values=(emnu[0], emnu[1]))
    flush()

    menubar = tk.Menu(window)
    # 菜单项目
    stu_menu = tk.Menu(menubar)
    menubar.add_cascade(label='查询', command=topdlog)
    menubar.add_cascade(label='添加', command=adddata)
    menubar.add_cascade(label='刷新', command=flush)
    menubar.add_cascade(label='退出', command=destry)
    window.config(menu=menubar)

    window.mainloop()


def logingui(window: Tk):
    # 设置标题
    window.title("密码本登录")
    # 获取屏幕尺寸
    s_width, s_height = window.maxsize()
    # 设置窗体大小
    w_width, w_height = 434, 250
    align = "%dx%d+%d+%d" % (w_width, w_height, (s_width - w_width) / 2, (s_height - w_height) / 2)
    # 设置位置以及屏幕居中
    window.geometry(align)


    # 设置标签
    account_label = tk.Label(window, text="账  户", font=('fangsong', 14))
    account_label.place(x=100, y=60)

    username_label = tk.Label(window, text="用户名", font=('fangsong', 14))
    username_label.place(x=100, y=100)

    password_label = tk.Label(window, text="密  码", font=('fangsong', 14))
    password_label.place(x=100, y=140)

    # 账户
    account_text = tk.StringVar()
    account_entry = tk.Entry(window, textvariable=account_text, font=('fangsong', 14),
                             width=15)
    account_entry.focus()
    account_entry.place(x=170, y=60)

    # 用户名
    username_text = tk.StringVar()
    username_entry = tk.Entry(window, textvariable=username_text, font=('fangsong', 14),
                              width=15)
    username_entry.place(x=170, y=100)

    # 密码
    password_text = tk.StringVar()
    password_entry = tk.Entry(window, textvariable=password_text, font=('fangsong', 14),
                              width=15, show="*")
    password_entry.place(x=170, y=140)

    def get_loginpars():
        account = account_text.get()
        username = username_text.get()
        password = password_text.get()

        if verblen([account, username, password]):

            dao_data = login((account, username))
            if dao_data:
                for enmu in dao_data:
                    if decryption(enmu[0]) == password and enmu[1] == "admin":
                        msgshowinfo("登录成功", "登录成功")
                        window.destroy()
                        window2 = Tk()
                        maingui(window2)
            else:
                msgshowinfo("登录失败", "账户、用户名或密码错误")

        else:
            msgshowinfo("登录失败", "请填写完整")


    login_btn = tk.Button(window, text="登录",
                          font=('fangsong', 14), command=get_loginpars)
    login_btn.config(width=10)
    login_btn.place(x=170, y=180)


window = Tk()
logingui(window)
window.mainloop()